package com.ygqh.baby.service.impl;

import com.alibaba.fastjson.JSON;
import com.wdt.api.exception.WdtException;
import com.wdt.api.response.StockQueryResponse;
import com.wdt.api.response.support.Stock;
import com.ygqh.baby.ao.Message;
import com.ygqh.baby.ao.ProductStatus;
import com.ygqh.baby.ao.QueryInfo;
import com.ygqh.baby.ao.ResultSet;
import com.ygqh.baby.mapper.YgStockMapper;
import com.ygqh.baby.model.*;
import com.ygqh.baby.po.YgSku;
import com.ygqh.baby.po.YgStock;
import com.ygqh.baby.po.YgStockExample;
import com.ygqh.baby.service.RedisCacheEvictService;
import com.ygqh.baby.service.WdtQybStockService;
import com.ygqh.baby.service.YgStockLogService;
import com.ygqh.baby.service.YgStockService;
import com.ygqh.baby.utils.WdtStockSyncUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author admin
 */
@Service
public class YgStockServiceImpl implements YgStockService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private YgStockMapper ygStockMapper;
    @Autowired
    private DocumentConstant documentConstant;
    @Autowired
    private YgStockLogService ygStockLogService;
    @Autowired
    private RedisCacheEvictService redisCacheEvictService;
    @Autowired
    private WdtQybStockService wdtQybStockService;

    private final ExecutorService executorService = Executors.newFixedThreadPool(5);

    @Override
    public List<YgStock> find() {
        YgStockExample example = new YgStockExample();
        example.createCriteria();
        return ygStockMapper.selectByExample(example);
    }

    @Override
    public int findCount() {
        YgStockExample example = new YgStockExample();
        example.createCriteria();
        return ygStockMapper.countByExample(example);
    }

    @Override
    public YgStock findById(Long id) {
        return ygStockMapper.selectByPrimaryKey(id);
    }

    @Override
    public int save(YgStock ygStock) {
        return ygStockMapper.insertSelective(ygStock);
    }

    @Override
    public int update(YgStock ygStock) {
        return ygStockMapper.updateByPrimaryKeySelective(ygStock);
    }

    @Override
    public ResultSet<YgStock> search(QueryInfo queryInfo, String q, Long groupId, String productName, String productCode, String skuCode, Long amount,
                                     Boolean isPre) {
        List<YgStock> list = ygStockMapper.selectSuper(queryInfo, q, groupId, productName, productCode, skuCode, amount, isPre);
        int count = ygStockMapper.countSuper(q, groupId, productName, productCode, skuCode, amount, isPre);
        ResultSet<YgStock> resultSet = new ResultSet<YgStock>(count, list);
        return resultSet;
    }

    @Override
    public ResultSet<StockModel> searchV2(QueryInfo queryInfo, String q, ProductStatus productStatus, String productName, String productCode, String skuCode,
                                          Long amount, Long supplierId, Boolean isPre, Boolean isSync, String adminProductName, String barCode) {
        List<StockModel> list = ygStockMapper.selectSuperV2(queryInfo, q, productStatus, productName, productCode, skuCode, amount, supplierId, isPre, isSync, adminProductName, barCode);
        int count = ygStockMapper.countSuperV2(q, productStatus, productName, productCode, skuCode, amount, supplierId, isPre, isSync, adminProductName, barCode);
        ResultSet<StockModel> resultSet = new ResultSet<StockModel>(count, list);
        return resultSet;
    }

    @Override
    public int deleteSoft(Long[] ids) {
        Assert.notEmpty(ids);
        int count = 0;
        for (Long id : ids) {
            YgStock ygStock = this.findById(id);
            if (ygStock == null) {
                continue;
            }
            count += ygStockMapper.updateByPrimaryKeySelective(ygStock);
        }

        return count;
    }

    @Override
    public int deleteSoft(Long id) {
        return this.deleteSoft(new Long[]{id});
    }

    @Override
    public int saveOrUpdate(YgStock ygStock) {
        int r = 0;
        if (ygStock.getId() != null) {
            r = this.update(ygStock);
        } else {
            r = this.save(ygStock);
        }
        return r;
    }

    @Override
    public YgStock findBySkuCode(String skuCode) {
        YgStockExample example = new YgStockExample();
        example.createCriteria().andSkuCodeEqualTo(skuCode);
        List<YgStock> list = ygStockMapper.selectByExample(example);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<YgStock> findBySkuCodes(String[] skuCodes) {
        if (skuCodes == null || skuCodes.length == 0) {
            return Collections.emptyList();
        }
        YgStockExample example = new YgStockExample();
        example.createCriteria().andSkuCodeIn(Arrays.asList(skuCodes));
        return ygStockMapper.selectByExample(example);
    }

    @Override
    public int reduceStock(List<YgStock> stocks, String from) {
        ygStockLogService.addStockLogBatch(stocks, from, "-1");
        return ygStockMapper.reduceStock(stocks);
    }

    @Override
    public Boolean validateStockNum(Long skuId, Long quantity) {
        return ygStockMapper.validateStockNum(skuId, quantity);
    }

    @Override
    public int backStock(List<YgStock> stockList, String from) {

        ygStockLogService.addStockLogBatch(stockList, from, "");
        return ygStockMapper.backStock(stockList);
    }

    @Override
    public int updateBatch(List<YgStock> stockList, String updateBy, String remark) {
        int updateBatch = ygStockMapper.updateBatch(stockList);
        ygStockLogService.addStockLogBatch(stockList, updateBy, remark);
        redisCacheEvictService.evictProductByStock(stockList, "stock.houtai.update");
        return updateBatch;
    }

    @Override
    public int updateBatchByTask(List<YgStock> stockList) {
        if (CollectionUtils.isEmpty(stockList)) {
            return 0;
        }
        List<String> skuCodes = stockList.stream().map(YgStock::getSkuCode).collect(Collectors.toList());

        List<YgStock> oldStock = this.findBySkuCodes(skuCodes.toArray(new String[skuCodes.size()]));

        for (YgStock newStock : stockList) {
            for (YgStock old : oldStock) {
                if (newStock.getSkuCode().equals(old.getSkuCode()) && !old.getIsSync()) {
                    newStock.setAmount(null);
                }
            }
        }
        int i = doUpdateBatchByTask(stockList);
        executorService.execute(() -> {
            ygStockLogService.addStockLogBatch(stockList, oldStock, "task", "");
            redisCacheEvictService.evictProductByStock(stockList, "stock.task.update");
        });
        return i;
    }

    private int doUpdateBatchByTask(List<YgStock> stockList) {
        if (!org.springframework.util.CollectionUtils.isEmpty(stockList)) {
            List<YgStock> list = new ArrayList<>(100);
            for (int i = 0; i < stockList.size(); i++) {
                list.add(stockList.get(i));
                if (i > 0 && (i % 200 == 0 || i == stockList.size() - 1)) {
                    ygStockMapper.updateBatchByTask(list);
                    list.clear();
                }
            }
        }
        return 1;
    }

    @Override
    public List<CheckStockModel> checkStock() {
        return ygStockMapper.checkStock();
    }

    @Override
    public List<YgSku> findByProductId(Long productId) {
        return ygStockMapper.findByProductId(productId);
    }

    @Override
    public YgStock findLastUpdate(String updateBy) {
        return ygStockMapper.selectLastUpdate(updateBy);
    }

    @Override
    public Message validateStockNum(List<YgShoppingCarModel> carList) {

        List<String> skuCodes = carList.stream().map(YgShoppingCarModel::getSkuCode).collect(Collectors.toList());
        List<YgStock> stockList = this.findBySkuCodes(skuCodes.toArray(new String[]{}));
        for (YgShoppingCarModel car : carList) {
            for (YgStock stock : stockList) {
                if (car.getSkuCode().equals(stock.getSkuCode())) {
                    if (car.getQuantity().compareTo(stock.getAmount()) > 0) {
                        return Message.error("【" + car.getProductName() + "】" + documentConstant.getSTOCK_LESS(), car.getProductName());
                    }
                }
            }
        }
        return Message.success("");
    }

    @Override
    public List<StockModel> findProductStock(Date startDate, Date endDate, Long amount) {

        return ygStockMapper.selectProductStock(startDate, endDate, amount);
    }

    @Override
    public List<StockModel> findSkuStockByProduct(List<String> list) {
        return ygStockMapper.selectSkuStockByProduct(list);
    }

    @Override
    public int setSync(String skuCode, Boolean sync) {
        YgStockExample example = new YgStockExample();
        example.createCriteria().andSkuCodeEqualTo(skuCode);

        YgStock stock = new YgStock();
        stock.setSkuCode(skuCode);
        stock.setIsSync(sync);
        return ygStockMapper.updateByExampleSelective(stock, example);
    }

    @Cacheable(cacheNames = "redis", key = "'stock:pcode_'+#productCode")
    @Override
    public List<YgStockModel> findByProductCode(String productCode) {
        return ygStockMapper.selectByProductCode(productCode);
    }

    @Override
    public List<YgStockModel> findByProductCodes(List<String> productCodes) {
        return ygStockMapper.selectByProductCodes(productCodes);
    }

    @Override
    public List<StockVo> findStockVoBySkuCodes(String... skuCodes) {
        return ygStockMapper.selectStockVoBySkuCodes(skuCodes);
    }

    @Override
    public List<StockVo> findStockVoByProductCodes(String... productCodes) {
        return ygStockMapper.selectStockVoByProductCodes(productCodes);
    }

    @Override
    public List<StockModel> getStockList(QueryInfo queryInfo, String q, ProductStatus productStatus, String productName, String productCode, String skuCode,
                                         Long amount, Long supplierId, Boolean isPre, Boolean isSync, String adminProductName, String barCode) {

        List<StockModel> list = ygStockMapper.selectSuperV2(queryInfo, q, productStatus, productName, productCode, skuCode, amount, supplierId, isPre, isSync, adminProductName, barCode);
        return list;
    }

    @Override
    public Message fullySyncStockFromWdt() {
        List<String> skuCodeList = ygStockMapper.getSkuCodeListForSyncStock();
        System.out.println("同步skuCode数：" + skuCodeList.size());
        return doSyncStock(skuCodeList, "fullySyncStockFromWdt");

    }

    @Override
    public Message syncStockFromWdt(String... skuCode) {
        if (skuCode == null || skuCode.length == 0) {
            return Message.error("请输入skuCode", skuCode);
        }
        return doSyncStock(Arrays.asList(skuCode), "syncStockFromWdt");
    }

    @Override
    public List<String> getSkuCodeListForSyncStock() {
        return ygStockMapper.getSkuCodeListForSyncStock();
    }

    private Message doSyncStock(List<String> skuCodeList, String updateFrom) {
        new Thread(() -> {
            Message message = doSyncStockSplit(skuCodeList, updateFrom);
            System.out.println(JSON.toJSON(message));
        }).start();
        return Message.success(null);
    }

    @Override
    public Message doSyncStockSplit(List<String> skuCodeList, String updateFrom) {
        List<Stock> wdtStockList = new ArrayList<>(skuCodeList.size());
        StringBuilder sb = new StringBuilder();
        for (String s : skuCodeList) {
            try {
                StockQueryResponse response = wdtQybStockService.stockQuery(s, WdtStockSyncUtil.WAREHOUSE_NOS);
                wdtStockList.addAll(response.getStocks());
            } catch (WdtException e) {
                sb.append(s).append("获取【").append(s).append("】旺店通库存失败。-->").append(e.getErrorMsg());
                logger.error(e.getMessage(), e);
            }
        }
        Date currDate = new Date();
        List<YgStock> stockList = new ArrayList<>(wdtStockList.size());
        Map<String, List<Stock>> collect = wdtStockList.stream().collect(Collectors.groupingBy(Stock::getSpecNo));
        collect.forEach((specNo, stocks) -> {
            BigDecimal realAmount = BigDecimal.ZERO;
            BigDecimal amount = BigDecimal.ZERO;
            for (Stock stock : stocks) {
                // real_amount（可发库存）=实际库存-预订单量-待审核量-未付款量-待发货量-锁定库存量
                realAmount = realAmount.add(WdtStockSyncUtil.getRealAmount(stock));
                //  计算后的 real_amount 大于1的情况下， real_amount *50% ，向上取整,更新 m 站的库存
                amount = amount.add(WdtStockSyncUtil.getAmount(realAmount));
            }
            stockList.add(new YgStock(specNo, amount.longValue(), realAmount.longValue(), currDate, updateFrom));
        });
        this.updateBatchByTask(stockList);
        if (sb.length() > 0) {
            return Message.error(sb.toString(), stockList.size());
        }
        return Message.success(stockList.size());
    }
}
